using AdminApiTemplate.Common.Exceptions;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using System.Text;

namespace AdminApiTemplate.Test
{
    public class Model2Sql
    {
        public static void Build(string input)
        {
            var parts = input.Split(',');
            var filepath = parts[0];
            if (!File.Exists(filepath))
            {
                throw new WithCodeException((int)ErrorCodeEnum.InvalidArguments, "文件不存在");
            }

            var typeName = "";
            if (parts.Length > 1)
            {
                typeName = parts[1];
            }

            var dbType = default(DBTypeEnum?);
            if (parts.Length > 2)
            {
                if (parts[2] == "2")
                {
                    dbType = DBTypeEnum.SqlServer;
                }
                else
                {
                    dbType = DBTypeEnum.MySql;
                }
            }

            var sb = new StringBuilder();
            var sb2 = new StringBuilder();
            var assembly = Assembly.LoadFile(filepath);
            var types = assembly.GetTypes();
            foreach (var t in types)
            {
                if (!t.IsClass)
                {
                    continue;
                }

                if (!string.IsNullOrEmpty(typeName))
                {
                    if (t.Name.ToLower() != typeName.ToLower())
                    {
                        continue;
                    }
                }

                var attr = t.GetCustomAttribute<TableAttribute>();
                if (attr != null)
                {
                    CreateTableSql(sb, t, attr.Name);
                    CreateTableSql2(sb2, t, attr.Name);
                }
            }

            var dt = DateTime.Now;
            var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

            var db1 = true;
            var db2 = true;
            if (dbType.HasValue)
            {
                switch (dbType)
                {
                    case DBTypeEnum.MySql:
                        {
                            db1 = true;
                            db2 = false;
                            break;
                        }
                    case DBTypeEnum.SqlServer:
                        {
                            db1 = false;
                            db2 = true;
                            break;
                        }
                }
            }

            if (db1)
            {
                var content = sb.ToString();
                var filename = string.Format("tables{0}.txt", dt.ToString("yyyyMMddHHmmss"));
                var fullname = Path.Combine(desktop, filename);
                File.WriteAllText(fullname, content, Encoding.UTF8);
            }

            if (db2)
            {
                var content2 = sb2.ToString();
                var filename2 = string.Format("tables{0}_2.txt", dt.ToString("yyyyMMddHHmmss"));
                var fullname2 = Path.Combine(desktop, filename2);
                File.WriteAllText(fullname2, content2, Encoding.UTF8);
            }
        }

        static void CreateTableSql(StringBuilder sb, Type t, string tableName)
        {
            var tableComment = "";
            var attr = t.GetCustomAttribute<DescriptionAttribute>();
            if (attr != null)
            {
                tableComment=$" COMMENT='{attr.Description}'";
            }

            sb.AppendFormat("CREATE TABLE `{0}`(\r\n", tableName);

            var ps = t.GetProperties();
            for (var i = 0; i < ps.Length; i++)
            {
                var p = ps[i];

                var comment = "";
                var descriptionAttr = p.GetCustomAttribute<DescriptionAttribute>();
                if (descriptionAttr != null)
                {
                    comment=$" COMMENT '{descriptionAttr.Description}'";
                }

                var columnName = p.Name;
                var columnAttr = p.GetCustomAttribute<ColumnAttribute>();
                if (columnAttr != null)
                {
                    columnName = columnAttr.Name;
                }

                var keyAttr = p.GetCustomAttribute<KeyAttribute>();
                if (keyAttr != null)
                {
                    sb.AppendFormat("    `{0}` INT NOT NULL PRIMARY KEY AUTO_INCREMENT", columnName);
                }
                else
                {
                    var requiredAtte = p.GetCustomAttribute<RequiredAttribute>();
                    if (requiredAtte != null)
                    {
                        sb.AppendFormat("    `{0}` {1} NOT NULL{2}", columnName, GetColumnType(p), comment);
                    }
                    else
                    {
                        sb.AppendFormat("    `{0}` {1} NULL{2}", columnName, GetColumnType(p), comment);
                    }
                }

                if (i < ps.Length - 1)
                {
                    sb.Append(",\r\n");
                }
                else
                {
                    sb.Append("\r\n");
                }
            }

            sb.AppendLine($"){tableComment};\r\n\r\n");
        }

        static void CreateTableSql2(StringBuilder sb, Type t, string tableName)
        {
            var attr = t.GetCustomAttribute<DescriptionAttribute>();
            if (attr != null)
            {
                sb.AppendLine($@"
-- =============================================
-- Author: Aqua
-- Create date: {DateTime.Now.ToString("yyyy/MM/dd")}
-- Description: {attr.Description}
-- =============================================");
            }

            sb.AppendFormat("CREATE TABLE [dbo].[{0}](\r\n", tableName);

            var remarks = new List<ColumnDescriptionItem>();
            var ps = t.GetProperties();
            for (var i = 0; i < ps.Length; i++)
            {
                var p = ps[i];

                var columnName = p.Name;
                var columnAttr = p.GetCustomAttribute<ColumnAttribute>();
                if (columnAttr != null)
                {
                    columnName = columnAttr.Name;
                }

                var descriptionAttr = p.GetCustomAttribute<DescriptionAttribute>();
                if (descriptionAttr != null)
                {
                    sb.AppendFormat("    /*{0}*/\r\n", descriptionAttr.Description);
                    remarks.Add(new ColumnDescriptionItem
                    {
                        Column = columnName,
                        Description = descriptionAttr.Description,
                        Table = tableName
                    });
                }

                var keyAttr = p.GetCustomAttribute<KeyAttribute>();
                if (keyAttr != null)
                {
                    sb.AppendFormat("    [{0}] BIGINT IDENTITY(1,1) PRIMARY KEY NOT NULL", columnName);
                }
                else
                {
                    var requiredAtte = p.GetCustomAttribute<RequiredAttribute>();
                    if (requiredAtte != null)
                    {
                        sb.AppendFormat("    [{0}] {1} NOT NULL", columnName, GetColumnType(p));
                    }
                    else
                    {
                        sb.AppendFormat("    [{0}] {1} NULL", columnName, GetColumnType(p));
                    }
                }

                if (i < ps.Length - 1)
                {
                    sb.Append(",\r\n\r\n");
                }
                else
                {
                    sb.Append("\r\n\r\n");
                }
            }

            sb.AppendLine(");\r\n\r\n");

            foreach (var remark in remarks)
            {
                sb.AppendLine($@"
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'{remark.Description}' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'{remark.Table}', @level2type=N'COLUMN',@level2name=N'{remark.Column}'
GO");
            }
        }

        static string GetColumnType(PropertyInfo p)
        {
            if (p.PropertyType == typeof(int) || p.PropertyType == typeof(int?) || p.PropertyType.IsEnum)
            {
                return "INT";
            }

            if (p.PropertyType == typeof(long) || p.PropertyType == typeof(long?))
            {
                return "BIGINT";
            }

            if (p.PropertyType == typeof(double) || p.PropertyType == typeof(double?))
            {
                return "DOUBLE";
            }

            if (p.PropertyType == typeof(decimal) || p.PropertyType == typeof(decimal?))
            {
                return "DECIMAL(18,2)";
            }

            if (p.PropertyType == typeof(DateTime) || p.PropertyType == typeof(DateTime?))
            {
                return "DATETIME";
            }

            if (p.PropertyType == typeof(bool) || p.PropertyType == typeof(bool?))
            {
                return "BIT";
            }

            var maxLengthAttr = p.GetCustomAttribute<MaxLengthAttribute>();
            if (maxLengthAttr != null)
            {
                if (maxLengthAttr.Length < 2000)
                {
                    return string.Format("VARCHAR({0})", maxLengthAttr.Length);
                }

                return "TEXT";
            }

            return "VARCHAR(50)";
        }
    }

    /// <summary>
    /// 字段描述
    /// </summary>
    public class ColumnDescriptionItem
    {
        /// <summary>
        /// 表
        /// </summary>
        public string Table { get; set; }

        /// <summary>
        /// 字段
        /// </summary>
        public string Column { get; set; }

        /// <summary>
        /// 描述
        /// </summary>
        public string Description { get; set; }
    }

    /// <summary>
    /// 数据库类型
    /// </summary>
    public enum DBTypeEnum
    {
        MySql = 1,

        SqlServer = 2
    }
}
